package com.study.springmvc.context;

import com.study.springmvc.anno.Autowired;
import com.study.springmvc.anno.Controller;
import com.study.springmvc.anno.Service;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.reflections.Reflections;


/**
 * @author lupengwei
 * <p>
 * BeanFactory 注解方式实现
 */
public class BeanFactory {

    private static final Map<String, Object> CONTEXT = new HashMap<>();
    
    
    public BeanFactory(Properties properties) {
        init(properties);
    }
    
    /**
     * 初始化容器，注意这里并没有考虑循环依赖问题
     */
    private void init(Properties properties) {
        System.out.println("初始化 BeanFactory...");
       
    
        // 读取需要扫描的包的路径
        String scanPackage = properties.getProperty("spring.scan.package");
        System.out.println("注解扫描的包路径 = " + scanPackage);
        Reflections reflections = new Reflections(scanPackage);

        // 创建 Bean
        Set<Class<?>> serviceClassSet = reflections.getTypesAnnotatedWith(Service.class);
        Set<Class<?>> controllerClassSet = reflections.getTypesAnnotatedWith(Controller.class);
        Set<Class<?>> allClassSet = new HashSet<>();
        allClassSet.addAll(serviceClassSet);
        allClassSet.addAll(controllerClassSet);
        for (Class<?> aClass : allClassSet) {
            Service serviceAnnotation = aClass.getAnnotation(Service.class);
            
            String beanName;
            if (serviceAnnotation != null) {
                // 如果没有自定义 beanName，使用用类型的名称作为 beanName，如 TestController 的 BeanName 为 testController
                beanName = serviceAnnotation.value();
                beanName = "".equals(beanName) ? getSimpleName(aClass) : beanName;
            } else {
                beanName = getSimpleName(aClass);
            }
            beanName = "".equals(beanName) ? getSimpleName(aClass) : beanName;
            
            try {
                System.out.println("创建 Bean, beanName = " + beanName + ", class = " + aClass.getName());
                Object instance = aClass.getDeclaredConstructor().newInstance();
                CONTEXT.put(beanName, instance);
            } catch (InstantiationException |
                IllegalAccessException |
                InvocationTargetException |
                NoSuchMethodException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }

        // 注入依赖
        for (Class<?> aClass : allClassSet) {
            // 获取这个类的全部字段，只有添加 @Autowired 注解的字段需要自动注入
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                // 获取自己
                String targetBeanName = getSimpleName(aClass);
                Object targetBean = CONTEXT.get(targetBeanName);
                if (targetBean == null) {
                    continue;
                }

                Autowired autowiredAnnotation = declaredField.getAnnotation(Autowired.class);
                if (autowiredAnnotation != null) {
                    // 如果没有自定义 beanName，就使用属性名称作为 beanName
                    String beanName = autowiredAnnotation.value();
                    if ("".equals(beanName)) {
                        beanName = declaredField.getName();
                    }

                    // 使用属性名称作为 beanName 获取 Bean，如果不存在就使用类型名称作为 beanName
                    Object injectBean = CONTEXT.get(beanName);
                    if (injectBean == null) {
                        beanName = getSimpleName(declaredField.getType());
                        injectBean = CONTEXT.get(beanName);
                    }

                    if (injectBean != null) {
                        declaredField.setAccessible(true);
                        try {
                            // 属性注入
                            declaredField.set(targetBean, injectBean);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
    }

    
    public Object getBean(String beanName) {
        return CONTEXT.get(beanName);
    }
    

    private String getSimpleName(Class<?> aClass) {
        return toLowerCaseFirstChar(aClass.getSimpleName());
    }


    private static String toLowerCaseFirstChar(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return Character.toLowerCase(s.charAt(0)) + s.substring(1);
        }
    }
}
